﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Collections;
using Palsta.Core.Manifest;
using Palsta.Core.exp;

namespace Palsta.Core.Manifest.Parser
{
    class ManifestParser
    {
        #region Members
        private Manifest mf;
        private String errorMsg;

        #endregion

        #region Constructors

        public ManifestParser() { }

        #endregion

        #region Accessors

        public virtual String ErrorMsg
        {
            get
            {
                return this.errorMsg;
            }
        }

        #endregion

        #region Methods

        public Manifest Parse(String path)
        {
            mf = new Manifest();
            XmlDocument xd = new XmlDocument();
            xd.Load(path);
            IEnumerator ienum = xd.GetEnumerator();
            while (ienum.MoveNext())
            {
                XmlNode node = (XmlNode)ienum.Current;
                if (node.Name.StartsWith("?"))
                {
                    continue;
                }
                else if (node.Name.Equals("xsf:xDocumentClass"))
                {
                    for (int i = 0; i < node.ChildNodes.Count; i++)
                    {
                        XmlNode n = node.ChildNodes[i];
                        if (n.Name.Equals("xsf:package"))
                        {
                            continue;
                        }
                        else if (n.Name.Equals("xsf:importParameters"))
                        {
                            continue;
                        }
                        else if (n.Name.Equals("xsf:documentVersionUpgrade"))
                        {
                            continue;
                        }
                        else if (n.Name.Equals("xsf:extensions"))
                        {
                            continue;
                        }
                        else if (n.Name.Equals("xsf:views"))
                        {
                            if (!this.ParseViews(n))
                            {
                                return null;
                            }
                        }
                        else if (n.Name.Equals("xsf:domEventHandlers"))
                        {
                            if (!this.ParseDomEventHandlers(n))
                            {
                                return null;
                            }
                        }
                        else if (n.Name.Equals("xsf:customValidation"))
                        {
                            if (!this.ParseCustomValidation(n))
                            {
                                return null;
                            }
                        }
                        else if (n.Name.Equals("xsf:ruleSets"))
                        {
                            if (!this.ParseRuleSets(n))
                            {
                                return null;
                            }
                        }
                        else if (n.Name.Equals("xsf:applicationParameters"))
                        {
                            continue;
                        }
                        else if (n.Name.Equals("xsf:documentSchemas"))
                        {
                            continue;
                        }
                        else if (n.Name.Equals("xsf:fileNew"))
                        {
                            continue;
                        }
                        else if (n.Name.Equals("xsf:calculations"))
                        {
                            if (!this.ParseCalculations(n))
                            {
                                return null;
                            }
                        }
                    }
                }
            }

            foreach (DomEventHandler eh in this.mf.EventHandlers)
            {
                RuleSet rs = null;
                foreach (RuleSet r in this.mf.RuleSets)
                {
                    if (r.Name.Equals(eh.RuleSetAction))
                    {
                        rs = r;
                        break;
                    }
                }
                if (rs == null)
                {
                    return null;
                }
            }
            return this.mf;
        }

        private Boolean ParseDomEventHandlers(XmlNode node)
        {
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                XmlNode n = node.ChildNodes[i];
                if (n.Name.Equals("xsf:domEventHandler"))
                {
                    String match = InfoPathXml.GetNodeAttr(n, "match");
                    if (match == null)
                    {
                        this.errorMsg = "<xsf:domEventHandler> without \"match\" attribute.";
                        return false;
                    }
                    DomEventHandler eh = new DomEventHandler(match);
                    for (int j = 0; j < n.ChildNodes.Count; j++)
                    {
                        XmlNode ruleSetAction = n.ChildNodes[j];
                        if (ruleSetAction.Name.Equals("xsf:ruleSetAction"))
                        {
                            String ruleSet = InfoPathXml.GetNodeAttr(ruleSetAction, "ruleSet");
                            if (ruleSet == null)
                            {
                                this.errorMsg = "<xsf:ruleSetAction> without \"ruleSet\" attribute.";
                                return false;
                            }
                            eh.RuleSetAction = ruleSet;
                        }
                    }
                    this.mf.EventHandlers.Add(eh);
                }

            }
            return true;
        }

        private Boolean ParseCustomValidation(XmlNode node)
        {
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                XmlNode n = node.ChildNodes[i];
                if (n.Name.Equals("xsf:errorCondition"))
                {
                    String match = InfoPathXml.GetNodeAttr(n, "match");
                    if (match == null)
                    {
                        this.errorMsg = "<xsf:errorCondition> without \"match\" attribute.";
                        return false;
                    }

                    String expressionContext = InfoPathXml.GetNodeAttr(n, "expressionContext");
                    if (expressionContext == null)
                    {
                        this.errorMsg = "<xsf:errorCondition> without \"expressionContext\" attribute.";
                        return false;
                    }

                    String expression = InfoPathXml.GetNodeAttr(n, "expression");
                    if (expression == null)
                    {
                        this.errorMsg = "<xsf:errorCondition> without \"expression\" attribute.";
                        return false;
                    }
                    CustomValidation cv = new CustomValidation(match, expressionContext, expression);

                    
                    for (int j = 0; j < n.ChildNodes.Count; j++)
                    {
                        XmlNode ruleSetAction = n.ChildNodes[j];
                        if (ruleSetAction.Name.Equals("xsf:errorMessage"))
                        {
                            String typeName = InfoPathXml.GetNodeAttr(ruleSetAction, "type");
                            if (typeName == null)
                            {
                                this.errorMsg = "<xsf:errorMessage> without \"type\" attribute.";
                                return false;
                            }
                            CustomValidationErrorMsg.ErrorMsgType type;
                            if (typeName.Equals("modeless"))
                            {
                                type = CustomValidationErrorMsg.ErrorMsgType.modeless;
                            }
                            else if (typeName.Equals("modal"))
                            {
                                type = CustomValidationErrorMsg.ErrorMsgType.modal;
                            }
                            else
                            {
                                this.errorMsg = "Invalid value for attribute \"type\" in <xsf:errorMessage>";
                                return false;
                            }

                            String shortMessage = InfoPathXml.GetNodeAttr(ruleSetAction, "shortMessage");
                            if (shortMessage == null)
                            {
                                this.errorMsg = "<xsf:errorMessage> without \"shortMessage\" attribute.";
                                return false;
                            }
                            CustomValidationErrorMsg cvem = new CustomValidationErrorMsg(type, shortMessage);
                            cv.ErrorMsg = cvem;
                        }
                        else if (ruleSetAction.Name.Equals("?caption"))
                        {
                            continue;

                        }
                    }
                    this.mf.CustomValidations.Add(cv);
                }

            }
            return true;
        }

        private Boolean ParseRuleSets(XmlNode node)
        {
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                XmlNode n = node.ChildNodes[i];
                if (n.Name.Equals("xsf:ruleSet"))
                {
                    String name = InfoPathXml.GetNodeAttr(n, "name");
                    if (name == null)
                    {
                        this.errorMsg = "<xsf:ruleSet> without \"name\" attribute.";
                        return false;
                    }
                    RuleSet rs = new RuleSet(name);
                    for (int j = 0; j < n.ChildNodes.Count; j++)
                    {
                        XmlNode ruleSetAction = n.ChildNodes[j];
                        if (ruleSetAction.Name.Equals("xsf:rule"))
                        {
                            String caption = InfoPathXml.GetNodeAttr(ruleSetAction, "caption");
                            if (caption == null)
                            {
                                this.errorMsg = "<xsf:rule> without \"caption\" attribute.";
                                return false;
                            }

                            String condition = InfoPathXml.GetNodeAttr(ruleSetAction, "condition");
                            if (condition == null)
                            {
                                this.errorMsg = "<xsf:rule> without \"condition\" attribute.";
                                return false;
                            }

                            String isEnabled = InfoPathXml.GetNodeAttr(ruleSetAction, "isEnabled");
                            if (isEnabled == null)
                            {
                                isEnabled = "yes";
                            }

                            Boolean bIsEnable;
                            if (isEnabled.Equals("yes"))
                            {
                                bIsEnable = true;
                            }
                            else if (isEnabled.Equals("no"))
                            {
                                bIsEnable = false;
                            }
                            else
                            {
                                this.errorMsg = "Invalid value for attribute \"isEnabled\" in <xsf:rule>.";
                                return false;
                            }

                            Rule r = new Rule(caption, condition, bIsEnable);
                            for (int k = 0; k < ruleSetAction.ChildNodes.Count; k++)
                            {
                                XmlNode ruleAction = ruleSetAction.ChildNodes[k];
                                if (ruleAction.Name.Equals("xsf:assignmentAction"))
                                {

                                    String targetField = InfoPathXml.GetNodeAttr(ruleAction, "targetField");
                                    if (targetField == null)
                                    {
                                        this.errorMsg = "<xsf:assignmentAction> without \"targetField\" attribute.";
                                        return false;
                                    }

                                    String expression = InfoPathXml.GetNodeAttr(ruleAction, "expression");
                                    if (expression == null)
                                    {
                                        this.errorMsg = "<xsf:assignmentAction> without \"expression\" attribute.";
                                        return false;
                                    }

                                    RuleSetAssignmentAction rsaa = new RuleSetAssignmentAction(targetField, expression);
                                    r.RuleActions.Add(rsaa);
                                }
                                else if (ruleAction.Name.Equals("xsf:dialogBoxMessageAction"))
                                {
                                    String msg = ruleAction.InnerText;
                                    RuleSetDialogBoxMessageAction rsDlgBoxMsg = new RuleSetDialogBoxMessageAction(msg);
                                    r.RuleActions.Add(rsDlgBoxMsg);
                                }
                                else if (ruleAction.Name.Equals("xsf:dialogBoxExpressionAction"))
                                {
                                    String exp = ruleAction.InnerText;
                                    RuleSetDialogBoxExpressionAction rsDlgBoxExp = new RuleSetDialogBoxExpressionAction(exp);
                                    r.RuleActions.Add(rsDlgBoxExp);
                                }

                            }
                            rs.Rules.Add(r);
                        }
                    }
                    this.mf.RuleSets.Add(rs);
                }

            }
            return true;
        }

        private Boolean ParseCalculations(XmlNode node)
        {
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                XmlNode n = node.ChildNodes[i];
                if (n.Name.Equals("xsf:calculatedField"))
                {
                    String target = InfoPathXml.GetNodeAttr(n, "target");
                    if (target == null)
                    {
                        this.errorMsg = "<xsf:calculatedField> without \"target\" attribute.";
                        return false;
                    }

                    String expression = InfoPathXml.GetNodeAttr(n, "expression");
                    if (expression == null)
                    {
                        this.errorMsg = "<xsf:calculatedField> without \"expression\" attribute.";
                        return false;
                    }

                    String refreshName = InfoPathXml.GetNodeAttr(n, "refresh");
                    if (refreshName == null)
                    {
                        this.errorMsg = "<xsf:calculatedField> without \"refresh\" attribute.";
                        return false;
                    }

                    Caculation.RefreshType refresh;
                    if (refreshName.Equals("onChange"))
                    {
                        refresh = Caculation.RefreshType.onChange;
                    }
                    else
                    {
                        this.errorMsg = "Invalid value for attribute \"refresh\" in <xsf:calculatedField>.";
                        return false;
                    }


                    Caculation c = new Caculation(target, expression, refresh);
                    this.mf.Caculations.Add(c);
                }
            }
            return true;
        }

        private Boolean ParseViews(XmlNode node)
        {
            String defaultView = InfoPathXml.GetNodeAttr(node, "default");
            if (defaultView == null)
            {
                this.errorMsg = "<xsf:views> without \"default\" attribute.";
                return false;
            }
            this.mf.DefaultView = defaultView;

            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                XmlNode n = node.ChildNodes[i];
                if (n.Name.Equals("xsf:view"))
                {
                    String name = InfoPathXml.GetNodeAttr(n, "name");
                    if (name == null)
                    {
                        this.errorMsg = "<xsf:view> without \"name\" attribute.";
                        return false;
                    }

                    String caption = InfoPathXml.GetNodeAttr(n, "caption");
                    if (caption == null)
                    {
                        this.errorMsg = "<xsf:view> without \"caption\" attribute.";
                        return false;
                    }

                    String showMenuItem = InfoPathXml.GetNodeAttr(n, "showMenuItem");
                    if (showMenuItem == null)
                    {
                        showMenuItem = "no";
                    }
                    Boolean bShowMenuItem;
                    if (showMenuItem.Equals("yes"))
                    {
                        bShowMenuItem = true;
                    }
                    else if (showMenuItem.Equals("no"))
                    {
                        bShowMenuItem = false;
                    }
                    else
                    {
                        this.errorMsg = "Invalid value for attribute \"showMenuItem\" in <xsf:view>.";
                        return false;
                    }

                    ViewDesc vd = new ViewDesc(name, caption, bShowMenuItem);
                    for (int j = 0; j < n.ChildNodes.Count; j++)
                    {
                        XmlNode mainpane = n.ChildNodes[j];
                        if (mainpane.Name.Equals("xsf:mainpane"))
                        {
                            String transform = InfoPathXml.GetNodeAttr(mainpane, "transform");
                            if (transform == null)
                            {
                                this.errorMsg = "<xsf:mainpane> without \"transform\" attribute.";
                                return false;
                            }
                            vd.Transform = transform;
                        }
                    }

                    this.mf.Views.Add(vd);
                }
            }
            return true;
        }


        #endregion

        #region Override Operators

        #endregion

        #region Static Methods

        #endregion

    }
}
